﻿using BenchmarkDotNet.Attributes;
using Prometheus;

namespace Benchmark.NetCore;

/// <summary>
/// This benchmark aims to show that accessing preexisting metrics using various label combinations is fast and does not allocate memory.
/// </summary>
[MemoryDiagnoser]
public class MetricLookupBenchmarks
{
    // Metric -> Variant -> Label values
    private static readonly string[][][] _labelValueRows;

    private const int _metricCount = 10;
    private const int _variantCount = 10;
    private const int _labelCount = 5;

    static MetricLookupBenchmarks()
    {
        _labelValueRows = new string[_metricCount][][];

        for (var metricIndex = 0; metricIndex < _metricCount; metricIndex++)
        {
            var variants = new string[_variantCount][];
            _labelValueRows[metricIndex] = variants;

            for (var variantIndex = 0; variantIndex < _variantCount; variantIndex++)
            {
                var values = new string[_labelCount];
                _labelValueRows[metricIndex][variantIndex] = values;

                for (var labelIndex = 0; labelIndex < _labelCount; labelIndex++)
                    values[labelIndex] = $"metric{metricIndex:D2}_label{labelIndex:D2}_variant{variantIndex:D2}";
            }
        }
    }

    private readonly CollectorRegistry _registry = Metrics.NewCustomRegistry();
    private readonly Counter[] _metrics;

    public MetricLookupBenchmarks()
    {
        _metrics = new Counter[_metricCount];

        var factory = Metrics.WithCustomRegistry(_registry);

        // Just use 1st variant for the keys (all we care about are that there is some name-like value in there).
        for (var metricIndex = 0; metricIndex < _metricCount; metricIndex++)
            _metrics[metricIndex] = factory.CreateCounter($"metric{metricIndex:D2}", "", _labelValueRows[metricIndex][0]);
    }

    /// <summary>
    /// Increments an unlabelled Collector instance for a single metric.
    /// </summary>
    [Benchmark]
    public void WithoutLabels_OneMetric_OneSeries()
    {
        _metrics[0].Inc();
    }

    /// <summary>
    /// Increments unlabelled Collector instances for a multiple metrics.
    /// </summary>
    [Benchmark]
    public void WithoutLabels_ManyMetrics_OneSeries()
    {
        for (var metricIndex = 0; metricIndex < _metricCount; metricIndex++)
            _metrics[metricIndex].Inc();
    }

    /// <summary>
    /// Increments a labelled Collector.Child instance for a single metric.
    /// </summary>
    [Benchmark]
    public void WithLabels_OneMetric_OneSeries()
    {
        _metrics[0].WithLabels(_labelValueRows[0][0]).Inc();
    }

    /// <summary>
    /// Increments labelled Collector.Child instances for one metric with multiple different sets of labels.
    /// </summary>
    [Benchmark]
    public void WithLabels_OneMetric_ManySeries()
    {
        for (var variantIndex = 0; variantIndex < _variantCount; variantIndex++)
            _metrics[0].WithLabels(_labelValueRows[0][variantIndex]).Inc();
    }

    /// <summary>
    /// Increments a labelled Collector.Child instance for multiple metrics.
    /// </summary>
    [Benchmark]
    public void WithLabels_ManyMetrics_OneSeries()
    {
        for (var metricIndex = 0; metricIndex < _metricCount; metricIndex++)
            _metrics[metricIndex].WithLabels(_labelValueRows[metricIndex][0]).Inc();
    }

    /// <summary>
    /// Increments labelled Collector.Child instances for multiple metrics with multiple different sets of labels.
    /// </summary>
    [Benchmark]
    public void WithLabels_ManyMetrics_ManySeries()
    {
        for (var metricIndex = 0; metricIndex < _metricCount; metricIndex++)
            for (var variantIndex = 0; variantIndex < _variantCount; variantIndex++)
                _metrics[metricIndex].WithLabels(_labelValueRows[metricIndex][variantIndex]).Inc();
    }
}
